#include "audio_sr.h"
// my_sr 是一个全局变量
my_sr_t my_sr;

void audio_sr_init(void)
{
    // 1.初始化语音识别模型. 参数就是存储模型的分区标签
    srmodel_list_t *models = esp_srmodel_init("model");
    // 2. 音频前端初始化配置  参数1:输出类型(一个麦克风) 参数2:语音识别模型 参数3: 语音识别类型(语音识别)
    afe_config_t *afe_config = afe_config_init("M", models, AFE_TYPE_SR, AFE_MODE_LOW_COST);

    // 补充: 前端优化 TODO
    afe_config->aec_init = false; // 回升消除
    afe_config->ns_init = false;  // 噪声抑制
    afe_config->se_init = false;  // 语音增强

    afe_config->vad_init = true;        // 语音活动检测
    afe_config->vad_mode = VAD_MODE_2;  // 数字越小, 越敏感
    afe_config->vad_min_noise_ms = 500; // 噪声检测阈值

    afe_config->wakenet_init = true;        // 语音唤醒
    afe_config->wakenet_mode = DET_MODE_90; // 唤醒模式: 值越大越容易被唤醒, 但是容易误唤醒

    /**
     * 这里原本用的是这个esp_afe_sr_iface_t但是太长了
     * 咱们自己定一个结构体，
     */
    // 获取句柄
    my_sr.afe_handle = esp_afe_handle_from_config(afe_config);
    // 创建实例
    my_sr.afe_data = my_sr.afe_handle->create_from_config(afe_config);
}

void feed_task(void *);
void fetch_task(void *);

// 回调函数1: 唤醒后的回调  回调函数2:当语音状态发声变化(说话->静音   静音->说话)
// 系统初始化器和任务调度器
void audio_sr_start(RingbufHandle_t ringBuf,
                    void (*wake_cb)(void),
                    void (*vad_change_cb)(vad_state_t))
{
    // 状态初始化
    my_sr.is_running = true;
    my_sr.is_wake = false;

    // 配置管理
    //  my_sr 是一个全局变量 存的指针在其他函数也可以用
    my_sr.wake_cb = wake_cb;
    my_sr.vad_change_cb = vad_change_cb;
    // 这里为什么要使用环形缓存区
    // xRingbufferSend和xRingbufferReceive有速度区别
    my_sr.ringBuf = ringBuf;

    // 启动两个任务:创建生产者-消费者任务对
    // 1. 喂数据: 从es8311读取音频数据, 给sr模型
    // 2. 从sr模型读取数据, 然后存入到环形缓冲区中

    // 时间片轮转：由于两个任务优先级相同，FreeRTOS 会给每个任务分配一个时间片（比如10ms）。当某个任务的时间片用完后，调度器就会切换到另一个任务。

    // 阻塞让出：在实际运行中，任务经常会主动或被动地"阻塞"（Block），从而让出CPU给其他任务：
    // feed_task 可能会在 bsp_sound_read() 处阻塞，等待硬件有可用的音频数据。
    // fetch_task 可能会在 my_sr.afe_handle->fetch() 处阻塞，等待AFE引擎有可用的处理结果。
    // 当任务阻塞时，调度器会立即切换到另一个就绪态的任务。
    //  xTaskCreate(feed_task, "feed_task", 4 * 1024, NULL, 5, NULL);
    //  xTaskCreate(fetch_task, "read_task", 4 * 1024, NULL, 5, NULL);
    //  任务函数，任务名称，堆栈深度，任务参数（null），任务优先级（5），任务句柄（null），运行核心
    xTaskCreateWithCaps(feed_task, "feed_task", 4 * 1024, NULL, 5, NULL, MALLOC_CAP_SPIRAM);
    xTaskCreateWithCaps(fetch_task, "fetch_task", 4 * 1024, NULL, 5, NULL, MALLOC_CAP_SPIRAM);
}
// 从es8311之中取出来放进sr之中，里面全是PCM
void feed_task(void *args)
{
    // 首先是从es8311之中获取的音频片段的大小
    int feed_chunksize = my_sr.afe_handle->get_feed_chunksize(my_sr.afe_data);
    // 获取通道数
    int feed_nch = my_sr.afe_handle->get_feed_channel_num(my_sr.afe_data);

    int size = feed_chunksize * feed_nch * sizeof(int16_t);
    // 存储音频片段的缓冲区
    int16_t *feed_buff = (int16_t *)malloc(size);

    while (my_sr.is_running)
    {
        // 从es8311获取音频片段
        bsp_sound_read(feed_buff, size);
        // 把获取的数据喂给sr模型
        my_sr.afe_handle->feed(my_sr.afe_data, feed_buff);
    }
    // 开辟的内存需要释放
    free(feed_buff);
    // 任务自我删除的调用
    vTaskDelete(NULL);
}

void fetch_task(void *args)
{
    while (my_sr.is_running)
    {
        // 获取语音识别后的结果
        afe_fetch_result_t *result = my_sr.afe_handle->fetch(my_sr.afe_data);
        // 具体的识别后的音频数据
        int16_t *processed_audio = result->data;
        // 当前语音状态:静音or说话
        vad_state_t vad_state = result->vad_state;
        // 唤醒状态：当前的语音是否在唤醒
        wakenet_state_t wakeup_state = result->wakeup_state;

        // 检测到唤醒词
        if (wakeup_state == WAKENET_DETECTED)
        {
            my_sr.is_wake = true;
            my_sr.last_vad_state = VAD_SILENCE; // 唤醒之后，vad设置为静音
            if (my_sr.wake_cb)
            {
                my_sr.wake_cb();
            }
        }

        // 处理vad
        if (my_sr.is_wake)
        {
            // 当状态变化时通知外界。如果状态没变也通知，就违背了这个函数的设计初衷，会导致接收方收到大量重复的无用事件
            // 有当语音状态真的从静音变为说话 (VAD_SILENCE -> VAD_SPEECH)，或者从说话变为静音 (VAD_SPEECH -> VAD_SILENCE) 时，才会进入 if 语句内部执行更新和回调。在状态稳定期间，这个判断条件为假，几乎不消耗任何额外资源。
            if (vad_state != my_sr.last_vad_state)
            {
                // 更新上一次的语音状态
                my_sr.last_vad_state = vad_state;

                if (my_sr.vad_change_cb)
                {
                    my_sr.vad_change_cb(vad_state);
                }
            }
        }
        // 系统收到音频：[...静音..., '你', '好', '世', '界', ...静音...]
        // 当收到 '好' 这个字时，VAD 根据 '你' 和 '好' 的音频特征，判断出当前是语音（vad_state = VAD_SPEECH）。
        //  处理说话的片段: 存入到环形缓冲区
        if (my_sr.is_wake && vad_state == VAD_SPEECH)
        {
            // if vad cache is exists, please attach the cache to the front of processed_audio to avoid data loss
            if (result->vad_cache_size > 0)
            {
                int16_t *vad_cache = result->vad_cache;
                if (my_sr.ringBuf)
                {
                    xRingbufferSend(my_sr.ringBuf, vad_cache, result->vad_cache_size, 0);
                }
            }
            // 向环形缓冲区存入数据
            if (my_sr.ringBuf)
            {
                
                xRingbufferSend(my_sr.ringBuf, processed_audio, result->data_size, 0);
            }
        }
    }
    vTaskDelete(NULL);
}
